/* 
 *	Copyright (C) 2007 cooleyes
 *	eyes.cooleyes@gmail.com 
 *
 *  This Program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *   
 *  This Program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU General Public License for more details.
 *   
 *  You should have received a copy of the GNU General Public License
 *  along with GNU Make; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
 *  http://www.gnu.org/copyleft/gpl.html
 *
 */


#include "mac_parser.h"
#include "common/mem64.h"
#include <mac/All.h> 
#include <mac/IO.h>
#include <mac/MACLib.h>
#include <mac/CharacterHelper.h>



#define SIRENS2_MAC_SAMPLES_PER_FRAME 1152

extern "C"  {void pspDebugScreenPrintf( const char* fmt, ... );}

typedef struct {
	CIO* io;
	IAPEDecompress* decompress;
	uint32_t next_frame;
} MacContext;

class BufferedIO : public CIO {
public:
	BufferedIO(buffered_reader_t* reader);
	~BufferedIO();

    	int Open(const wchar_t * pName);
	int Close();
    
	// read / write
	int Read(void * pBuffer, unsigned int nBytesToRead, unsigned int * pBytesRead);
	int Write(const void * pBuffer, unsigned int nBytesToWrite, unsigned int * pBytesWritten);
    
	// seek
	int Seek(int nDistance, unsigned int nMoveMode);
    
	// other functions
	int SetEOF();

	// creation / destruction
	int Create(const wchar_t * pName);
	int Delete();

	// attributes
	int GetPosition();
	int GetSize();
	int GetName(wchar_t * pBuffer);
private:
	buffered_reader_t* reader;
};

BufferedIO::BufferedIO(buffered_reader_t* reader) {
	this->reader = reader;
}

BufferedIO::~BufferedIO() {
	Close();
}

int BufferedIO::Open(const wchar_t * pName) {
	return 0;
}

int BufferedIO::Close() {
	this->reader = 0;
	return 0;
}

int BufferedIO::Read(void * pBuffer, unsigned int nBytesToRead, unsigned int * pBytesRead) {
	*pBytesRead = buffered_reader_read(this->reader, pBuffer, nBytesToRead);
	return 0;
}

int BufferedIO::Write(const void * pBuffer, unsigned int nBytesToWrite, unsigned int * pBytesWritten) {
	return ERROR_IO_WRITE;
}

int BufferedIO::Seek(int nDistance, unsigned int nMoveMode) {
	if( nMoveMode == 0 )
		buffered_reader_seek(this->reader, nDistance);
	else if ( nMoveMode == 1 )
		buffered_reader_seek(this->reader, 
			nDistance + buffered_reader_position(this->reader) );
	else 
		buffered_reader_seek(this->reader,
			buffered_reader_length(this->reader) + nDistance );
	return 0;
}

int BufferedIO::SetEOF() {
	return 0;
}

int BufferedIO::Create(const wchar_t * pName) {
	return 0;
}

int BufferedIO::Delete() {
	return 0;
}

int BufferedIO::GetPosition() {
	return buffered_reader_position(this->reader);
}

int BufferedIO::GetSize() {
	return buffered_reader_length(this->reader);
}

int BufferedIO::GetName(wchar_t * pBuffer) {
	return 0;
}

MonkeysAudioParser::MonkeysAudioParser() {
	initialize();
};

MonkeysAudioParser::~MonkeysAudioParser() {
	close();
};

void MonkeysAudioParser::initialize() {
	
	is_open = false;
	
	total_samples = 0;
	
	reader = 0;
	mac_context = 0;
	output_buffer = 0;
	
	memset(&init_data, 0, sizeof(AudioCodecInitData));
};

void MonkeysAudioParser::finalize() {
	if ( mac_context ) {
		MacContext* context = (MacContext*)mac_context;
		if ( context->decompress ) {
			delete context->decompress;
			context->decompress = 0;
		}
		if ( context->io ) {
			delete context->io;
			context->io = 0;
		}
		free_64(mac_context);
	}
	if ( reader ) {
		buffered_reader_close(reader);
	}
	if ( output_buffer ) {
		free(output_buffer);
	}
	initialize();
};

char* MonkeysAudioParser::open(const char* filename) {
	
	close();
	
	reader = buffered_reader_open(filename, 262144, 1);
	if ( !reader ) {
		finalize();
		return "MonkeysAudioParser : open buffered_reader fail";
	}
	buffered_reader_seek(reader, 0);
	
	mac_context = malloc_64(sizeof(MacContext));
	if ( mac_context == 0 ) {
		finalize();
		return "MonkeysAudioParser : malloc MacContext fail";
	}
	memset(mac_context, 0, sizeof(MacContext));
	MacContext* context = (MacContext*)mac_context;
	
	context->io = new BufferedIO(reader);
	if( context->io == 0 ) {
		finalize();
		return "MonkeysAudioParser : new BufferedIO fail";
	}
	
	int error_code;
	
	context->decompress = CreateIAPEDecompressEx(context->io, &error_code);
	
	if ( !context->decompress || error_code != ERROR_SUCCESS ) {
		finalize();
		return "MonkeysAudioParser : CreateIAPEDecompressEx fail";
	}
		
	
	init_data.channels = context->decompress->GetInfo(APE_INFO_CHANNELS);
	init_data.samplerate = context->decompress->GetInfo(APE_INFO_SAMPLE_RATE);
	init_data.sample_bits = context->decompress->GetInfo(APE_INFO_BITS_PER_SAMPLE);
	
	if ( init_data.channels < 1 || init_data.channels > 2 || init_data.sample_bits != 16 ) {
		finalize();
		return "MonkeysAudioParser : channels != 1 && channels != 2 || sample_bits != 16";
	}
	
	init_data.samples_per_frame = SIRENS2_MAC_SAMPLES_PER_FRAME;
	init_data.samples_per_decoded = init_data.samples_per_frame;
	init_data.frame_align = init_data.samples_per_frame * (16 >> 3) * 2;
	
	total_samples = context->decompress->GetInfo(APE_INFO_TOTAL_BLOCKS);

	output_buffer = malloc_64(init_data.frame_align);
	if ( output_buffer == 0 ) {
		finalize();
		return "MonkeysAudioParser : malloc output_buffer fail";
	}
	
	is_open = true;
	return 0;
};

void MonkeysAudioParser::close() {
	if ( is_open ) {
		finalize();
	}
};
	
uint32_t MonkeysAudioParser::get_samplerate() {
	return init_data.samplerate;
};

uint32_t MonkeysAudioParser::get_channels() {
	return init_data.channels;
};

uint64_t MonkeysAudioParser::get_duration() {
	uint64_t duration = 1000000LL*total_samples/init_data.samplerate;
	return duration;
};
	
uint32_t MonkeysAudioParser::get_samples_per_frame() {
	return init_data.samples_per_frame;
};

uint32_t MonkeysAudioParser::get_max_samples_per_frame() {
	return init_data.samples_per_frame;
};

uint64_t MonkeysAudioParser::seek_time(uint64_t time) {
	uint32_t samples = (uint32_t)(time*init_data.samplerate/1000000LL);
	if ( samples > total_samples )
		samples = total_samples;
	MacContext* context = (MacContext*)mac_context;
	context->decompress->Seek(samples);
	uint64_t current = 1000000LL*samples/init_data.samplerate;
	return current;
};

char* MonkeysAudioParser::get_frame(void** pcm, uint32_t* samples) {
	
	if ( !is_open )
		return "MonkeysAudioParser : parser is not open";
		
	MacContext* context = (MacContext*)mac_context;
	
	memset(output_buffer, 0, init_data.frame_align);
	
	uint32_t get_samples = 0;
	
	context->decompress->GetData((char*)output_buffer, init_data.samples_per_frame, (int*)&get_samples);
	
	*pcm = output_buffer;
	*samples = get_samples;
	return 0;
};





